class B(Exception):
    pass


class C(B):
    pass


class D(C):
    pass


for cls in [B, C, D]:
    try:
        # raise关键字抛出异常时，可以直接指定异常类名，也可以使用异常类的实例对象，raise cls()得到一样的结果
        raise cls
    except D:
        print("D", end=" ");
    except C:
        print("C", end=" ");
    except B:
        print("B", end=" ");

try:
    raise Exception("乱弹琴", 123)
except Exception as inst:
    print(type(inst), type(inst.args))  # the exception type
    print(inst.args)  # arguments stored in .args
    print(inst)  # __str__ allows args to be printed directly,
    # but may be overridden in exception subclasses
    x, y = inst.args  # unpack args
    print('x =', x)
    print('y =', y)
    print(inst.args[0], inst.args[1])
    # raise RuntimeError from None # 禁用异常链
    raise RuntimeError from Exception  # 表示异常由Exception触发
else:
    print("执行try语句没有抛异常后执行该语句");
finally:
    print("不管怎样，最后都会执行的语句")
    # 如果存在 finally 子句，则 finally 子句是
    # try 语句结束前执行的最后一项任务。不论 try 语句是否触发异常，都会执行 finally 子句。以下内容介绍了几种比较复杂的触发异常情景：
    #
    # 如果执行
    # try 子句期间触发了某个异常，则某个 except 子句应处理该异常。如果该异常没有 except 子句处理，在 finally 子句执行后会被重新触发。
    #
    # except 或 else 子句执行期间也会触发异常。 同样，该异常会在 finally 子句执行之后被重新触发。
    #
    # 如果 finally 子句中包含
    # break、continue
    # 或
    # return 等语句，异常将不会被重新引发。
    #
    # 如果执行
    # try 语句时遇到 break, 、continue 或 return 语句，则 finally 子句在执行 break、continue 或 return 语句之前执行。
    #
    # 如果 finally 子句中包含
    # return 语句，则返回值来自 finally 子句的某个
    # return 语句的返回值，而不是来自
    # try 子句的 return 语句的返回值。
    # with 语句支持以及时、正确的清理的方式使用文件对象：